//==============================================================================
//	AdjacencyListGraph2.cc
//
//==============================================================================
//.......1.........2.........3.........4.........5.........6.........7.........8
//

#include "AdjacencyListGraph2.hh"


//------------------------------------------------------------------------------
// * Print
//------------------------------------------------------------------------------
// Prints a representation of the graph to a file
//
void 
AdjacencyListGraph2::Print(string FileName){
  ofstream outFile;
  outFile.open(FileName.c_str());
  if (outFile.fail()) {
	 cout<<FileName<<" open failed."<<endl;
	 return;
  }
  Print(outFile);
  outFile.close();
}

//------------------------------------------------------------------------------
// * Print
//------------------------------------------------------------------------------
// Prints a representation of the graph to a stream (could be cout). 
//
void 
AdjacencyListGraph2::Print(ostream &outFile){
  NodeHash::iterator it;
  for(it = begin();it != end();it++){
	 (*it).second.Print(outFile);
  }	 
};

//------------------------------------------------------------------------------
// * ParseLine
//------------------------------------------------------------------------------
// Each line has format:
//
// NodeName  EdgeName EdgeWeight  EdgeName EdgeWeight ...
//
void 
AdjacencyListGraph2::ParseLine(string line,AdjacencyListGraph2Node &N){
  int offset = 0;
  N.mName = GetNextWSDelimitedString(line,offset,offset);
  if ((offset < 0) || (offset >= line.size())) {
	 N.mName = "";
	 return;
  }
  AdjacencyListGraph2Edge E;
  do{
	 E.mName = GetNextWSDelimitedString(line,offset,offset);
	 string weight = GetNextWSDelimitedString(line,offset,offset);	
	 if (weight != "") E.mWeight = atoi(weight.c_str());		
	 N.mEdges[E.mName]=E;
  }while((offset<line.size())&&(offset >=0));
}

//------------------------------------------------------------------------------
// * AddEdge
//------------------------------------------------------------------------------
// 
//
void
AdjacencyListGraph2::AddEdge(string NodeName,string EdgeName,double Weight)
{
  AdjacencyListGraph2Edge E;
  E.mName = EdgeName;
  E.mWeight = Weight;

  // If the node does not exist, add it 
  // otherwise simply add an edge to the existing node
  NodeHash::iterator nodeIt;
  nodeIt = find(NodeName);
  if (nodeIt == end()){
	 AdjacencyListGraph2Node N;
	 N.mName = NodeName;
	 N.mEdges[E.mName] = E;
	 (*this)[NodeName] = N; 
  }else{
	 // If the edge does not already exist, add it, otherwise do nothing
	 EdgeHash::iterator edgeIt;
	 edgeIt = (*nodeIt).second.mEdges.find(EdgeName);
	 if (edgeIt == (*nodeIt).second.mEdges.end()){
		(*nodeIt).second.mEdges[E.mName]=E;
	 }
  }
}


//------------------------------------------------------------------------------
// * Add
//------------------------------------------------------------------------------
// Adds the edges from FileName to the current graph
//
void 
AdjacencyListGraph2::Add(string FileName){
  ifstream fin;
  char buffer[100000];
  
  fin.open(FileName.c_str());
  do{
	 string line;
	 //GetLine(fin,line);
	 fin.getline(buffer,99999);
	 line = buffer;
	 StripLeadingSpaces(line);
	 if (line.size() > 0){
		AdjacencyListGraph2Node N;
		ParseLine(line,N);
		if (N.mName != ""){
		  NodeHash::iterator it;
		  it = find(N.mName);
		  // If the node isn't already in the graph, then simply add it. 
		  if (it == end()){		  
			 (*this)[N.mName] = N;
		  }else{
			 // Otherwise, add the edges of this node to the proper node. 
			 EdgeHash::iterator edgeIt,findIt;
			 for(edgeIt = N.mEdges.begin(); edgeIt != N.mEdges.end();edgeIt++){
				string EdgeName = (*edgeIt).second.mName;
				findIt = (*it).second.mEdges.find(EdgeName);
				// If the edge isn't already there, add it. 
				// KJD This should probably add the maximum weight edge
				if (findIt == (*it).second.mEdges.end()){
				  (*it).second.mEdges[EdgeName] = (*edgeIt).second;
				}else{
				  float oldWt = (*findIt).second.mWeight;
				  float newWt = (*edgeIt).second.mWeight;
				  if (newWt > oldWt){
					 (*findIt).second.mWeight = newWt;
				  }
				}
			 }
		  }
		}
	 }
  }while(!fin.eof());
}




//------------------------------------------------------------------------------
// * Read
//------------------------------------------------------------------------------
// Read the graph from a file
//
void 
AdjacencyListGraph2::Read(string FileName){
  ifstream fin;
  char buffer[100000];
  
  fin.open(FileName.c_str());
  do{
	 string line;
	 //GetLine(fin,line);
	 fin.getline(buffer,99999);
	 line = buffer;
	 StripLeadingSpaces(line);
	 if (line.size() > 0){
		AdjacencyListGraph2Node N;
		ParseLine(line,N);
		if (N.mName != ""){
		  (*this)[N.mName] = N;
		}
	 }
  }while(!fin.eof());
}

//------------------------------------------------------------------------------
// * Write
//------------------------------------------------------------------------------
// Save the graph to a file.
//
void 
AdjacencyListGraph2::Write(string FileName){
  ofstream outFile;
  outFile.open(FileName.c_str());
  NodeHash::iterator it;
  for(it = begin();it != end();it++){
	 (*it).second.Print(outFile);
  }
  outFile.close();
}


//------------------------------------------------------------------------------
// * Contains
//------------------------------------------------------------------------------
//
bool
AdjacencyListGraph2::Contains(string NodeName,string EdgeName){
  NodeHash::iterator NodeIt;
  EdgeHash::iterator EdgeIt;
  NodeIt = find(NodeName);
  if (NodeIt != end()){
	 EdgeIt = (*NodeIt).second.mEdges.find(EdgeName);
	 if (EdgeIt != (*NodeIt).second.mEdges.end()) return(true);
	 else return(false);
  }else{
	 return(false); // Node does not exist. 
  }
 
}



//------------------------------------------------------------------------------
// * GetEdgeDiffs
//------------------------------------------------------------------------------
//
void
AdjacencyListGraph2::GetEdgeDiffs(AdjacencyListGraph2 &TestGraph,
											 vector<string> &MissingNodes,vector<string> &MissingEdges,
											 vector<string> &ExtraNodes,vector<string> &ExtraEdges)
{
  // Find all the edges of TestGraph that occur in this graph.  TP
  // and the edges of TestGraph that do not occur in this graph FP
  NodeHash::iterator nodeIt;
  EdgeHash::iterator edgeIt;
  for(nodeIt = TestGraph.begin();nodeIt != TestGraph.end();nodeIt++){
	 string NodeName = (*nodeIt).second.mName;
	 for(edgeIt = (*nodeIt).second.mEdges.begin();edgeIt != (*nodeIt).second.mEdges.end();edgeIt++){
		string EdgeName = (*edgeIt).second.mName;
		if (!Contains(NodeName,EdgeName)){
		  ExtraNodes.push_back(NodeName);
		  ExtraEdges.push_back(EdgeName);
		}
	 }
  }

  for(nodeIt = begin();nodeIt != end();nodeIt++){
	 string NodeName = (*nodeIt).second.mName;
	 for(edgeIt = (*nodeIt).second.mEdges.begin();edgeIt != (*nodeIt).second.mEdges.end();edgeIt++){
		string EdgeName = (*edgeIt).second.mName;
		if (!TestGraph.Contains(NodeName,EdgeName)){
		  MissingNodes.push_back(NodeName);
		  MissingEdges.push_back(EdgeName);
		}
	 }
  }

}

//------------------------------------------------------------------------------
// * CompareGraphs
//------------------------------------------------------------------------------
// CompareGraphs
//
void 
AdjacencyListGraph2::CompareGraphs(AdjacencyListGraph2 &TestGraph,
											  long &TP,long &FP,long &MissingEdges,long &TotalTrueEdges,
											  double &sensitivity,double &veracity)
{
  TP = 0;
  FP = 0;
  MissingEdges = 0;
  TotalTrueEdges=0;

  // Find all the edges of TestGraph that occur in this graph.  TP
  // and the edges of TestGraph that do not occur in this graph FP
  NodeHash::iterator nodeIt;
  EdgeHash::iterator edgeIt;
  for(nodeIt = TestGraph.begin();nodeIt != TestGraph.end();nodeIt++){
	 for(edgeIt = (*nodeIt).second.mEdges.begin();edgeIt != (*nodeIt).second.mEdges.end();edgeIt++){
		if (Contains((*nodeIt).second.mName,(*edgeIt).second.mName)) TP++;
		else FP++;
	 }
  }

  for(nodeIt = begin();nodeIt != end();nodeIt++){
	 for(edgeIt = (*nodeIt).second.mEdges.begin();edgeIt != (*nodeIt).second.mEdges.end();edgeIt++){
		if (!TestGraph.Contains((*nodeIt).second.mName,(*edgeIt).second.mName)) MissingEdges++;
		TotalTrueEdges++;
	 }
  }

  double BogusEdgeRate = (double)FP/(double)(TP+FP);
  veracity = 1.0-BogusEdgeRate;
  sensitivity = (double)TP/(double)TotalTrueEdges;
}




//------------------------------------------------------------------------------
// * GetMinEdgeWeight
//------------------------------------------------------------------------------
// GetMinEdgeWeight
//
double
AdjacencyListGraph2::GetMinEdgeWeight()
{
  double minWeight = 999999.0;
  NodeHash::iterator nodeIt;
  EdgeHash::iterator edgeIt;
  for(nodeIt = begin();nodeIt != end();nodeIt++){
	 for(edgeIt = (*nodeIt).second.mEdges.begin();edgeIt != (*nodeIt).second.mEdges.end();edgeIt++){  
		if ((*edgeIt).second.mWeight < minWeight){
		  minWeight = (*edgeIt).second.mWeight;
		}
	 }
  }
  return(minWeight);
}

//------------------------------------------------------------------------------
// * RemoveEdges
//------------------------------------------------------------------------------
// RemoveEdges
//
int
AdjacencyListGraph2::RemoveEdges(double MinEdgeWeight)
{
  int removeCount = 0;
  NodeHash::iterator nodeIt;
  EdgeHash::iterator edgeIt;
  for(nodeIt = begin();nodeIt != end();nodeIt++){
	 vector<EdgeHash::iterator> KillEdges;
	 for(edgeIt = (*nodeIt).second.mEdges.begin();edgeIt != (*nodeIt).second.mEdges.end();edgeIt++){  
		if ((*edgeIt).second.mWeight < MinEdgeWeight){
		  KillEdges.push_back(edgeIt);
		}
	 }
	 for(int i = 0;i< KillEdges.size();i++){
		(*nodeIt).second.mEdges.erase(KillEdges[i]);
		removeCount++;
	 }
	 KillEdges.clear(); // Just in case. 
  }
  return(removeCount);
}


//------------------------------------------------------------------------------
// * CompareGraphsWithCutoff
//------------------------------------------------------------------------------
// CompareGraphsWithCutoff
//
void 
AdjacencyListGraph2::CompareGraphsWithCutoff(AdjacencyListGraph2 &TestGraph,
											  long &TP,long &FP,long &MissingEdges,long &TotalTrueEdges,
											  double &sensitivity,double &veracity,int MinOverlap)
{
  TP = 0;
  FP = 0;
  MissingEdges = 0;
  TotalTrueEdges=0;

  // Find all the edges of TestGraph that occur in this graph.  TP
  // and the edges of TestGraph that do not occur in this graph FP
  NodeHash::iterator TestNodeIt;
  EdgeHash::iterator TestEdgeIt;
  for(TestNodeIt = TestGraph.begin();TestNodeIt != TestGraph.end();TestNodeIt++){
	 for(TestEdgeIt = (*TestNodeIt).second.mEdges.begin();
		  TestEdgeIt != (*TestNodeIt).second.mEdges.end();TestEdgeIt++){
		// Only want to look for edges meeting the minimum overlap. 
		if ((*TestEdgeIt).second.mWeight > MinOverlap){
		  if (Contains((*TestNodeIt).second.mName,(*TestEdgeIt).second.mName)) TP++;
		  else FP++;
		}
	 }
  }

  NodeHash::iterator nodeIt;
  EdgeHash::iterator edgeIt;
  for(nodeIt = begin();nodeIt != end();nodeIt++){
	 for(edgeIt = (*nodeIt).second.mEdges.begin();edgeIt != (*nodeIt).second.mEdges.end();edgeIt++){
		if (!TestGraph.Contains((*nodeIt).second.mName,(*edgeIt).second.mName)){
		  MissingEdges++;
		}else{
		  // Only add an edge to the total edge count if it meets the minimum overlap. 
		  if ((*edgeIt).second.mWeight >= MinOverlap){ 
			 TotalTrueEdges++;
		  }
		}
	 }
  }

  double BogusEdgeRate = (double)FP/(double)TotalTrueEdges;
  veracity = 1.0-BogusEdgeRate;
  sensitivity = (double)TP/(double)TotalTrueEdges;
}

